#pragma once
#include <WinBase.h>

#define _USE_CRITICAL_SECTION_

#ifdef _USE_CRITICAL_SECTION_
class vmpi_spin_lock
{
public:
	vmpi_spin_lock() { InitializeCriticalSectionAndSpinCount(&cs_lock, 4000); }
	~vmpi_spin_lock() { DeleteCriticalSection(&cs_lock); }
	__forceinline void SelfInit(){ InitializeCriticalSectionAndSpinCount(&cs_lock, 4000); }
	__forceinline void SelfDone() { DeleteCriticalSection(&cs_lock); }
	__forceinline void Lock()
	{
		EnterCriticalSection(&cs_lock);
	}
	__forceinline void Unlock()
	{
		LeaveCriticalSection(&cs_lock);
	}
	__forceinline bool TestLock()
	{
		return TryEnterCriticalSection(&cs_lock) ? true : false;
	}
protected:
	CRITICAL_SECTION cs_lock;
};
#else
class vmpi_spin_lock
{
public:
	vmpi_spin_lock() { lock = 0; }
	__forceinline void SelfInit() { lock = 0; }
	__forceinline void SelfDone() {}
	__forceinline void Lock()
	{
		intptr_t tries = 0;
		while (InterlockedCompareExchange(lock, 1, 0) != 0)
		{
			++tries;
			// We used to reset to zero if we got to 100. This resets to 0 at 64 instead, with no branch.
			tries &= 63;
			// if tries == 0, we just rolled over 64, so we Sleep(1) to give other threads a chance to run... otherwise we Sleep(0)
			::Sleep(tries == 0);
		}
	}
	__forceinline void Unlock()
	{
		InterlockedExchange(&lock, 0);
	}
	__forceinline bool TestLock()
	{
		return InterlockedCompareExchange(&lock, 1, 0) == 0;
	}
protected:
	volatile uintptr_t lock;
};

#endif
